package com.tsy.common.utils;

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.zip.ZipException;

public class AntZipUtil {

    /**
     * 缓冲大小
     */
    private static final int BUFFER_SIZE = 2 << 9;


    /**
     * 压缩
     *
     * @param paths
     * @param zipFilePath
     */
    public static void zip(List<String> paths, String zipFilePath) {
        ZipOutputStream zos = null;
        try {
            zos = new ZipOutputStream(Files.newOutputStream(Paths.get(zipFilePath)));
            for (String filePath : paths) {
                // 递归压缩文件
                File file = new File(filePath);
                String relativePath = file.getName();
                if (file.isDirectory()) {
                    relativePath += File.separator;
                }
                zipFile(file, relativePath, zos);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (zos != null) {
                    zos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void zip(File sorceFile, String zipFilePath) {
        ZipOutputStream zos = null;
        try {
            zos = new ZipOutputStream(Files.newOutputStream(Paths.get(zipFilePath)));
            // 递归压缩文件
            String relativePath = sorceFile.getName();
            if (sorceFile.isDirectory()) {
                relativePath += File.separator;
            }
            zipFile(sorceFile, relativePath, zos);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (zos != null) {
                    zos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 保留空文件夹
     * @param file
     * @param relativePath
     * @param zos
     */
    public static void zipFile(File file, String relativePath, ZipOutputStream zos) {
        InputStream is = null;
        try {
            if (!file.isDirectory()) {
                ZipEntry zp = new ZipEntry(relativePath);
                zos.putNextEntry(zp);
                is = Files.newInputStream(file.toPath());
                byte[] buffer = new byte[BUFFER_SIZE];
                int length;
                while ((length = is.read(buffer)) >= 0) {
                    zos.write(buffer, 0, length);
                }
                zos.setEncoding(StandardCharsets.UTF_8.name());//解决文件名中文乱码
                zos.flush();
                zos.closeEntry();
            } else {
                // 添加空文件夹的条目
                if (file.listFiles().length == 0) {
                    ZipEntry zp = new ZipEntry(relativePath);
                    zos.putNextEntry(zp);
                    zos.closeEntry();
                } else {
                    String tempPath;
                    for (File f : Objects.requireNonNull(file.listFiles())) {
                        tempPath = relativePath + f.getName();
                        if (f.isDirectory()) {
                            tempPath += File.separator;
                        }
                        zipFile(f, tempPath, zos);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 不保留空文件夹
     * @param file
     * @param relativePath
     * @param zos
     */
    public static void zipFile1(File file, String relativePath, ZipOutputStream zos) {
        InputStream is = null;
        try {
            if (!file.isDirectory()) {
                ZipEntry zp = new ZipEntry(relativePath);
                zos.putNextEntry(zp);
                is = Files.newInputStream(file.toPath());
                byte[] buffer = new byte[BUFFER_SIZE];
                int length;
                while ((length = is.read(buffer)) >= 0) {
                    zos.write(buffer, 0, length);
                }
                zos.setEncoding(StandardCharsets.UTF_8.name());//解决文件名中文乱码
                zos.flush();
                zos.closeEntry();
            } else {
                String tempPath;
                for (File f : Objects.requireNonNull(file.listFiles())) {
                    tempPath = relativePath + f.getName();
                    if (f.isDirectory()) {
                        tempPath += File.separator;
                    }
                    zipFile1(f, tempPath, zos);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 智能解压缩
     * 压缩包包含多个文件，则创建压缩包同名目录作为首文件夹
     * 压缩包只包含一个文件，则直接解压
     *
     * @param zipFilePath
     * @param path
     */
    public static List<String> unzip1(String zipFilePath, String path) {
        FileOutputStream fos = null;
        InputStream is = null;
        List<String> filePaths = new ArrayList<>();
        try {
            ZipFile zf = new ZipFile(new File(zipFilePath), StandardCharsets.UTF_8.name());
            Enumeration<? extends ZipEntry> en = zf.getEntries();
            List<ZipEntry> entries = new ArrayList<>();

            boolean createFolder = false;
            // 将 Enumeration 转换为 List
            HashSet<String> set = new HashSet<>();
            while (en.hasMoreElements()) {
                ZipEntry zipEntry = en.nextElement();
                entries.add(zipEntry);
                if (!zipEntry.getName().contains("/")) {
                    createFolder = true;
                    continue;
                }
                set.add(zipEntry.getName().split("/")[0]);
                if (set.size() > 1) {
                    createFolder = true;
                }
            }

            String basePath = path;
            if (createFolder) {
                // 如果有多个文件，则创建一个与压缩包同名的文件夹
                String zipFileName = new File(zipFilePath).getName();
                String folderName = zipFileName.substring(0, zipFileName.lastIndexOf('.'));
                basePath = path + File.separator + folderName;
                new File(basePath).mkdirs();
            }

            for (ZipEntry zn : entries) {
                if (!zn.isDirectory()) {
                    is = zf.getInputStream(zn);
                    File f = new File(basePath + File.separator + zn.getName());
                    File file = f.getParentFile();
                    file.mkdirs();
                    fos = new FileOutputStream(f);
                    int len;
                    byte[] buffer = new byte[BUFFER_SIZE];
                    while (-1 != (len = is.read(buffer))) {
                        fos.write(buffer, 0, len);
                    }
                    fos.close();
                    filePaths.add(f.getAbsolutePath());
                }
            }
        } catch (ZipException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != is) {
                    is.close();
                }
                if (null != fos) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return filePaths;
    }

    /**
     * 解压缩
     *
     * @param zipFilePath
     * @param path
     */
    public static List<String> unzip(String zipFilePath, String path) {
        FileOutputStream fos = null;
        InputStream is = null;
        List<String> filePaths = new ArrayList<>();
        try {
            ZipFile zf = new ZipFile(new File(zipFilePath), StandardCharsets.UTF_8.name());
            Enumeration<?> en = zf.getEntries();
            while (en.hasMoreElements()) {
                ZipEntry zn = (ZipEntry) en.nextElement();
                if (!zn.isDirectory()) {
                    is = zf.getInputStream(zn);
                    File f = new File(path + File.separator + zn.getName());
                    File file = f.getParentFile();
                    file.mkdirs();
                    fos = new FileOutputStream(f);
                    int len;
                    byte buffer[] = new byte[BUFFER_SIZE];
                    while (-1 != (len = is.read(buffer))) {
                        fos.write(buffer, 0, len);
                    }
                    fos.close();
                    filePaths.add(f.getAbsolutePath());
                }
            }
        } catch (ZipException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != is) {
                    is.close();
                }
                if (null != fos) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return filePaths;
    }


    /**
     * 有如下目录结构：
     * D:\测试\文档.zip
     * D:\测试\说明.pdf
     * 把“说明.pdf”添加到“文档.zip”里面，生成一个新压缩包“文档(新).zip”。
     *
     * @param args
     */
    public static void main(String[] args) throws IOException {
        String folderPath = "C:\\Users\\Lenovo\\Desktop\\compress\\es";
        Path folder = Paths.get(folderPath);
        long size = 0;
        try {
            size = Files.walk(folder)
                    .filter(p -> p.toFile().isFile())
                    .mapToLong(p -> p.toFile().length())
                    .sum();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("文件夹大小: " + size + " 字节");
        String path = "C:\\Users\\Lenovo\\Desktop\\compress\\es\\es\\testf\\7d75db70c50208cf40a2acab5ffa2dc8.mp3";
        System.out.println(path.substring("C:\\Users\\Lenovo\\Desktop\\compress\\es\\es".length() + 1));


        /**
         * 压缩文件
         */
        String sourceDir = "C:\\Users\\Lenovo\\Desktop\\compress\\es2\\test";
        String sourceDir1 = "C:\\Users\\Lenovo\\Desktop\\compress\\es2\\testf";
        String targetDir="C:\\Users\\Lenovo\\Desktop\\compress";
        String zipName = targetDir + "\\es1.zip";
        try {
            List<String> list = new ArrayList<>();
            list.add(sourceDir);
            list.add(sourceDir1);
//            zip(list, zipName);
            List<String> unzip = unzip(zipName, "C:\\Users\\Lenovo\\Desktop\\compress");
            unzip.forEach(System.out::println);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}
